﻿#region copyright
/*
 * Author: Jan Vilimek: jan (at) vilimek (dot) cz
 * If you use any code from this program/library, please include above line
 * 
 */
#endregion

#region usings
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using pdb2mobiGUI.Properties;
using System.IO;
using System.Diagnostics;
using System.Runtime.InteropServices;
#endregion

namespace pdb2mobiGUI
{
	public partial class MainForm : Form
	{

		public MainForm()
		{
			InitializeComponent();
		}

		private void btnInputFolderSelect_Click(object sender, EventArgs e)
		{
			if (FolderDialogInput.ShowDialog() == DialogResult.OK)
			{
				Settings.Default.InputSelectedPath = FolderDialogInput.SelectedPath;
				this.LoadSettings();
				Settings.Default.Save();
			}
		}

		private void btnOutputFolderSelect_Click(object sender, EventArgs e)
		{
			if (FolderDialogOutput.ShowDialog() == DialogResult.OK)
			{
				Settings.Default.OutputSelectedPath = FolderDialogOutput.SelectedPath;
				this.LoadSettings();
				Settings.Default.Save();
			}
		}

		private void MainForm_Load(object sender, EventArgs e)
		{
			this.LoadSettings();
		}

		private void LoadSettings()
		{
			this.txtOutputFolder.Text = Settings.Default.OutputSelectedPath;
			this.txtInputFolder.Text = Settings.Default.InputSelectedPath;
		}

		private void btnStartBatchConversion_Click(object sender, EventArgs e)
		{
			if (backgroundWorker1.IsBusy)
				return;
			if (string.IsNullOrEmpty(this.txtInputFolder.Text) || string.IsNullOrEmpty(this.txtOutputFolder.Text))
			{
				MessageBox.Show(this, "Please specify input and output folder!", "conversion failed");
				return;
			}
			this.progressBar1.Value = 0;
			this.txtLog.BackColor = Color.FromKnownColor(KnownColor.Info);
			this.txtLog.ForeColor = Color.FromKnownColor(KnownColor.InfoText);
			this.txtLog.Text = "";
			this.backgroundWorker1.RunWorkerAsync();
			SetFormState(false);
		}

		private void SetFormState(bool enabled)
		{
			this.btnInputFolderSelect.Enabled = enabled;
			this.btnStartBatchConversion.Enabled = enabled;
			this.btnOutputFolderSelect.Enabled = enabled;
		}

		private StringBuilder currentConsoleOutput;

		private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
		{
			try
			{
				var bgWorker = (BackgroundWorker)sender;
				var support_apps_dir = Prepare();

				var inPath = Settings.Default.InputSelectedPath;
				if (inPath[inPath.Length - 1] != '\\')
					inPath += "\\";
				var outPath = Settings.Default.OutputSelectedPath;
				if (outPath[outPath.Length - 1] != '\\')
					outPath += "\\";
				var files = Directory.EnumerateFiles(inPath, "*.pdb", SearchOption.AllDirectories).ToArray();
				var filesCount = files.Length;
				var currentStep = 0;
				foreach (var fin in files)
				{
					if (bgWorker.CancellationPending)
						break;
					var pdbFile = new PDBFile();
					if (pdbFile.Read(fin, false))
					{
						var fout = fin.Replace(inPath, outPath);
						var foutPath = Path.GetDirectoryName(fout);
						if (!Directory.Exists(foutPath))
							Directory.CreateDirectory(foutPath);
						var tmpFileTxt = fin + ".txt";
						var tmpFileOpf = fout + ".opf";
						var tmpFileHtml = fout + ".html";
						var tmpFileCover = fout + ".gif";
						var fileLog = fout + ".log";
						var titleAll = Path.GetFileNameWithoutExtension(fout).Replace('_', ' ');
						var title = (titleAll.Contains('-') ? titleAll.Substring(0, titleAll.IndexOf('-')) : titleAll).Trim();
						var author = (titleAll.Contains('-') ? titleAll.Substring(titleAll.IndexOf('-') + 1) : "unknown").Trim();
						try
						{
							currentConsoleOutput = pdbFile.Log;
							var pExitCode = StartProcess(bgWorker, fin, pdbFile,
								Path.Combine(support_apps_dir, "pdb2txt.exe"),
								string.Format(@"-utf8 ""{0}""", fin));
							if (pExitCode == 0)
							{
								using (var fileHtmlWriter = File.CreateText(tmpFileHtml))
								{
									fileHtmlWriter.WriteLine(@"<!DOCTYPE html PUBLIC ""-//W3C//DTD HTML 4.01 Transitional//EN"">");
									fileHtmlWriter.WriteLine(@"<html>");
									fileHtmlWriter.WriteLine(@"<head>");
									fileHtmlWriter.WriteLine(@"<title>{0}</title>", titleAll);
									fileHtmlWriter.WriteLine(@"<meta http-equiv=""Content-Type"" content=""text/html; charset=UTF-8"" />");
									fileHtmlWriter.WriteLine(@"</head>");
									fileHtmlWriter.WriteLine(@"<body>");
									var lines = File.ReadAllLines(tmpFileTxt, Encoding.UTF8);
									foreach (var line in lines)
										fileHtmlWriter.WriteLine(@"<p>{0}</p>", line);
									fileHtmlWriter.WriteLine(@"</body>");
								}
								File.WriteAllText(tmpFileOpf, String.Format(Encoding.UTF8.GetString(Resources.mobigen),
									title, "cs", Guid.NewGuid(), author, tmpFileHtml, tmpFileCover), Encoding.UTF8);
								File.WriteAllBytes(tmpFileCover, Resources.cover_gif);
								pExitCode = StartProcess(bgWorker, fin, pdbFile,
									Path.Combine(support_apps_dir, "mobigen.exe"),
									string.Format(@"-unicode ""{0}""", tmpFileOpf));
							}
						}
						finally
						{
							File.Delete(tmpFileTxt);
							File.Delete(tmpFileOpf);
							File.Delete(tmpFileHtml);
							File.Delete(tmpFileCover);
							File.WriteAllText(fileLog, pdbFile.Log.ToString(), Encoding.UTF8);
						}
						//abcpalm.exe &""""&$fileIn&""" "&""""&$htmlFile&""" "&"1", @ScriptDir, @SW_HIDE)
						//mobigen.exe
						//mobi2mobi.exe --title
					}
					bgWorker.ReportProgress(currentStep++ * 100 / filesCount, pdbFile);
					//if (currentStep > 20)
					//        return;
				}
			}
			catch (Exception ex)
			{
				e.Result = ex;
			}
		}

		private int StartProcess(BackgroundWorker bgWorker, string fin, PDBFile pdbFile, string exeName, string arguments)
		{
			using (var proc = new Process())
			{
				proc.StartInfo.FileName = exeName;
				proc.StartInfo.Arguments = arguments;
				proc.StartInfo.RedirectStandardOutput = true;
				proc.StartInfo.RedirectStandardError = true;
				proc.StartInfo.UseShellExecute = false;
				proc.EnableRaisingEvents = true;
				proc.StartInfo.CreateNoWindow = true;
				// see below for output handler
				proc.ErrorDataReceived += proc_DataReceived;
				proc.OutputDataReceived += proc_DataReceived;

				proc.Start();

				proc.BeginErrorReadLine();
				proc.BeginOutputReadLine();
				while (!bgWorker.CancellationPending)
				{
					if (proc.WaitForExit(500))
						break;
					//if (proc.MainWindowHandle != IntPtr.Zero)
					//{
					//        //var winHandle = User32.FindWindow(null, "ABC Amber Palm Converter v2.04");
					//        //if (winHandle <= 0)
					//        //{
					//        //        pdbFile.Error = new InvalidOperationException(String.Format("could not find abcpalm window"));
					//        //        break;
					//        //}
					//        //User32.SendMessage(new IntPtr(winHandle), User32.WM.WM_CLOSE, 0, 0);
					//        //User32.SendMessage(new IntPtr(winHandle), User32.WM.WM_KEYDOWN, (int)User32.VK.VK_ENTER, 0);
					//        //User32.SendMessage(new IntPtr(winHandle), User32.WM.WM_KEYUP, (int)User32.VK.VK_ENTER, 0);
					//        //proc.WaitForExit();
					//        // a window is displayed.. probably an error..just kill the program and set error...
					//        //pdbFile.Error = new InvalidOperationException(String.Format("abcpalm displayed unknown error"));
					//        //break;
					//}
				}
				if (!proc.HasExited)
				{
					proc.Kill();
					proc.WaitForExit();
				}
				if (proc.ExitCode != 0)
					pdbFile.Error = new InvalidOperationException(String.Format("{0} exited with unknown error code {1}", Path.GetFileName(exeName), proc.ExitCode));
				return proc.ExitCode;
			}
		}

		private string Prepare()
		{
			var localAppData = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData, Environment.SpecialFolderOption.Create);
			var support_apps_dir = Path.Combine(localAppData, "pdb2mobi", "support_apps");
			if (!Directory.Exists(support_apps_dir))
			{
				Directory.CreateDirectory(support_apps_dir);
			}
			var files = Directory.EnumerateFiles("support_apps", "*.*");
			foreach (var file in files)
			{
				var target = Path.Combine(support_apps_dir, Path.GetFileName(file));
				if (!File.Exists(target))
					File.Copy(file, target, false);
			}
			return support_apps_dir;
		}

		private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
		{
			var pdbfile = (PDBFile)e.UserState;
			this.progressBar1.Value = e.ProgressPercentage;
			//var log = pdbfile.Log.ToString();//.Replace("\r", "").Replace("\n", "\r\n");
			this.txtLog.SelectionBackColor = (pdbfile.Error != null) ? Color.Red : Color.White;
			int start = this.txtLog.TextLength;

			this.txtLog.AppendText(String.Format("{0} ", pdbfile.originalFileName)); //\r\n{1}\r\n	,log
			if (pdbfile.Error != null)
			{
				this.txtLog.AppendText(String.Format("!! failed, error: {0}\r\n\r\n", pdbfile.Error.Message));
				int end = this.txtLog.TextLength;
				this.txtLog.Select(start, end - start);
				this.txtLog.SelectionColor = Color.Yellow;
				this.txtLog.SelectionBackColor = Color.Red;
				this.txtLog.SelectionLength = 0; 
				//this.txtLog.BackColor = Color.Red;
				//this.txtLog.ForeColor = Color.Yellow;
			}
			else
				this.txtLog.AppendText(String.Format("... converted OK\r\n\r\n"));

		}

		private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
		{
			SetFormState(true);
			var err = (e.Result as Exception) ?? e.Error;
			if (err != null)
				MessageBox.Show(this, err.ToString(), "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
			else
				MessageBox.Show(this, "Completed!");
		}

		public void proc_DataReceived(object sender, DataReceivedEventArgs e)
		{
			currentConsoleOutput.AppendLine(e.Data);
		}
	}
}


//TODO:
//01. ignore allready transformed books
//02. add CANCEL button...
//03. add support for PNRd etc formats...
//04. save global log to..somewhere?